/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.huawei.healthecology.processor;

import com.huawei.healthecology.api.BluetoothCentral;
import com.huawei.healthecology.data.bluetooth.response.BluetoothResponseCode;
import com.huawei.healthecology.log.LogUtil;
import com.huawei.healthecology.subscriber.BluetoothDeviceDiscoverySubscriber;

import lombok.Builder;
import ohos.app.Context;
import ohos.bluetooth.BluetoothHost;
import ohos.bluetooth.BluetoothRemoteDevice;
import ohos.event.commonevent.CommonEventManager;
import ohos.event.commonevent.CommonEventSubscribeInfo;
import ohos.event.commonevent.MatchingSkills;
import ohos.rpc.RemoteException;

import org.jetbrains.annotations.NotNull;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * Bluetooth central processor
 */
public class BluetoothCentralProcessor implements BluetoothCentral<BluetoothRemoteDevice>, HealthEcologyProcessor {
    private static final String TAG = "BluetoothCentralProcessor";

    private final Context contextInstance;

    private BluetoothHost bluetoothHost;

    private BluetoothDeviceDiscoverySubscriber discoverySubscriber;

    /**
     * constructor
     *
     * @param context Context
     */
    @Builder(builderMethodName = "hiddenBuilder")
    public BluetoothCentralProcessor(@NotNull Context context) {
        this.contextInstance = context;
    }

    /**
     * BluetoothCentralProcessor Builder
     *
     * @param context Context
     * @return BluetoothCentralProcessor
     */
    public static BluetoothCentralProcessorBuilder builder(Context context) {
        return hiddenBuilder().context(context);
    }

    @Override
    public void initProcessor() {
        this.bluetoothHost = BluetoothHost.getDefaultHost(contextInstance);
    }

    @Override
    public void releaseResource() {
        cancelDiscovery();
    }

    @Override
    public void destroyProcessor() {
        discoverySubscriber = null;
        bluetoothHost = null;
    }

    @Override
    public BluetoothResponseCode startDiscovery() {
        try {
            discoverySubscriber = new BluetoothDeviceDiscoverySubscriber(getDiscoverySubscribeInfo());
            CommonEventManager.subscribeCommonEvent(discoverySubscriber);
        } catch (RemoteException e) {
            LogUtil.error(TAG, "Subscribe Discovery bluetooth event failed: " + e.getCause().getLocalizedMessage());
        }
        return Optional.ofNullable(bluetoothHost)
            .filter(host -> (discoverySubscriber != null))
            .filter(BluetoothHost::startBtDiscovery)
            .map(result -> BluetoothResponseCode.OPERATION_SUCCESS)
            .orElse(BluetoothResponseCode.OPERATION_FAILED);
    }

    @Override
    public BluetoothResponseCode cancelDiscovery() {
        try {
            if (discoverySubscriber != null) {
                CommonEventManager.unsubscribeCommonEvent(discoverySubscriber);
            }
        } catch (RemoteException e) {
            LogUtil.error(TAG, "Subscribe Discovery bluetooth event failed: " + e.getCause().getLocalizedMessage());
        }
        return Optional.ofNullable(bluetoothHost)
            .filter(BluetoothHost::cancelBtDiscovery)
            .map(success -> BluetoothResponseCode.OPERATION_SUCCESS)
            .orElse(BluetoothResponseCode.OPERATION_FAILED);
    }

    @Override
    public BluetoothResponseCode pairDevice(String deviceId) {
        return Optional.ofNullable(bluetoothHost)
            .map(host -> host.getRemoteDev(deviceId))
            .filter(BluetoothRemoteDevice::startPair)
            .map(result -> BluetoothResponseCode.OPERATION_SUCCESS)
            .orElse(BluetoothResponseCode.PAIR_DEVICE_FAILED);
    }

    @Override
    public Stream<BluetoothRemoteDevice> getPairedDevices() {
        return Optional.ofNullable(bluetoothHost)
            .map(host -> host.getPairedDevices().stream())
            .orElse(Stream.empty());
    }

    private CommonEventSubscribeInfo getDiscoverySubscribeInfo() {
        MatchingSkills matchingSkills = new MatchingSkills();
        matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_STARTED);
        matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_FINISHED);
        matchingSkills.addEvent(BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED);
        return new CommonEventSubscribeInfo(matchingSkills);
    }
}
